<!--
  speedy-vision.js
  GPU-accelerated Computer Vision for JavaScript
  Copyright 2020-2024 Alexandre Martins <alemartf(at)gmail.com>

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
  
      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

  convolution.html
  Image convolution
-->
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="description" content="speedy-vision.js: GPU-accelerated Computer Vision for JavaScript">
        <meta name="author" content="Alexandre Martins">
        <title>Cool effects with image convolutions</title>
        <link href="demo-base.css" rel="stylesheet">
        <script src="demo-base.js"></script>
        <script src="../dist/speedy-vision.js"></script>
    </head>
    <body>
        <h1>Image convolution</h1>
        <form>
            <div>
                Select the
                <select id="template"></select>
                convolution kernel or edit the fields below:
            </div>
            <div class="separator"></div>
            <div>
                <table>
                    <tr>
                        <td><input type="text" id="kernel-0" value="0"></td>
                        <td><input type="text" id="kernel-3" value="0"></td>
                        <td><input type="text" id="kernel-6" value="0"></td>
                    </tr>
                    <tr>
                        <td><input type="text" id="kernel-1" value="0"></td>
                        <td><input type="text" id="kernel-4" value="1"></td>
                        <td><input type="text" id="kernel-7" value="0"></td>
                    </tr>
                    <tr>
                        <td><input type="text" id="kernel-2" value="0"></td>
                        <td><input type="text" id="kernel-5" value="0"></td>
                        <td><input type="text" id="kernel-8" value="0"></td>
                    </tr>
                </table>
            </div>
        </form>
        <div>
            <span id="status"></span>
            <canvas id="canvas-demo"></canvas>
        </div>
        <div>
            <button id="play">Play / pause</button>
        </div>
        <video id="video"
            poster="../assets/loading.jpg"
            width="426" height="240"
            loop muted autoplay playsinline hidden
            title="Free video by Chris Munnik from https://www.pexels.com/pt-br/video/3297379/">
                <source src="../assets/jelly.webm" type="video/webm" />
                <source src="../assets/jelly.mp4" type="video/mp4" />
        </video>
        <script>
// Convolution kernels written in column-major format (3x3)
const template = {
    "Original":
    [
        0, 0, 0,
        0, 1, 0,
        0, 0, 0,
    ],

    "Brighten":
    [
        0, 0, 0,
        0, 2, 0,
        0, 0, 0,
    ],

    "Simple blur":
    [
        0.11111, 0.11111, 0.11111,
        0.11111, 0.11111, 0.11111,
        0.11111, 0.11111, 0.11111,
    ],

    "Gaussian blur":
    [
        0.0625, 0.125, 0.0625,
        0.125, 0.25, 0.125,
        0.0625, 0.125, 0.0625,
    ],

    "Sharpen":
    [
         0,-1, 0,
        -1, 5,-1,
         0,-1, 0,
    ],

    "Emboss":
    [
        -2,-1, 0,
        -1, 1, 1,
         0, 1, 2,
    ],

    "Edges":
    [
        1, 1, 1,
        1,-8, 1,
        1, 1, 1,
    ],

    "Horizontal lines":
    [
        -1, 2,-1,
        -1, 2,-1,
        -1, 2,-1,
    ],

    "Vertical lines":
    [
        -1,-1,-1,
         2, 2, 2,
        -1,-1,-1,
    ],

    "Scharr (X)":
    [
        -3,-10,-3,
         0,  0, 0,
         3, 10, 3,
    ],

    "Scharr (Y)":
    [
        -3, 0, 3,
       -10, 0, 10,
        -3, 0, 3,
    ],

    "Laplacian":
    [
        0, 1, 0,
        1,-4, 1,
        0, 1, 0,
    ],
};

window.onload = async function()
{
    // input fields
    const kernelElement = [...(new Array(9)).keys()].map(k => document.getElementById('kernel-' + k));

    // setup the <select> element
    const templateSelector = document.getElementById('template');
    for(let templateName in template) {
        const option = document.createElement('option');
        const text = document.createTextNode(templateName);
        option.value = templateName;
        option.selected = (option.value == 'Original');
        option.appendChild(text);
        templateSelector.appendChild(option);
    }
    templateSelector.onchange = () => {
        const name = templateSelector.value;
        kernelElement.forEach((inputField, i) => inputField.value = template[name][i]);
        updatePipeline();
    };

    // update the pipeline as soon as a text field is changed
    for(let inputField of kernelElement)
        inputField.oninput = updatePipeline;

    // load the video
    const video = document.getElementById('video');
    const media = await Speedy.load(video);

    // play/pause
    const playButton = document.getElementById('play');
    playButton.onclick = () => video.paused ? video.play() : video.pause();

    // create a pipeline
    const pipeline = Speedy.Pipeline();
    const source = Speedy.Image.Source();
    const sink = Speedy.Image.Sink();
    const convolution = Speedy.Filter.Convolution();

    source.media = media;

    source.output().connectTo(convolution.input());
    convolution.output().connectTo(sink.input());

    pipeline.init(source, sink, convolution);

    // update the pipeline
    function updatePipeline()
    {
        const entries = kernelElement.map(field => Number(field.value));
        convolution.kernel = Speedy.Matrix(3, 3, entries);
    }

    // Main loop
    (function() {
        const canvas = setupCanvas('canvas-demo', media.width, media.height, video.title);
        let image = null, frameReady = false;

        async function update()
        {
            const result = await pipeline.run();
            image = result.image;

            frameReady = true;
            setTimeout(update, 1000 / 60);
        }
        update();

        function render()
        {
            if(frameReady) {
                renderMedia(image, canvas);
            }

            frameReady = false;
            requestAnimationFrame(render);
        }
        render();

        setInterval(renderStatus, 200);
    })();
}
        </script>
        <section id="speedy-vision">
            <span>Powered by <a href="https://github.com/alemart/speedy-vision" target="_blank">speedy-vision.js</a></span>
            <a href="https://github.com/sponsors/alemart" target="_blank" class="button">
                <img alt="GitHub Sponsors" src="https://img.shields.io/github/sponsors/alemart?style=for-the-badge&logo=github&label=Sponsor&labelColor=royalblue&color=gold">
            </a>
        </section>
    </body>
</html>